package core

import (
	"bytes"
	"errors"
	"fmt"
	uuid "github.com/satori/go.uuid"
	"net"
	"sync"
	"time"
	"tio-proxy-agent/log"
)

type ChannelContext struct {
	Id    string
	props struct {
		sync.Mutex
		data map[string]interface{}
	}
	BindIp                string
	BindPort              int
	LastByteBuffer        *bytes.Buffer
	NewReceivedByteBuffer chan []byte
	SendData              chan Packet
	config                GioConfig
}

func NewChannelContext(bindIp string, bindPort int, config GioConfig) *ChannelContext {
	ctx := &ChannelContext{
		Id: uuid.NewV4().String(),
		props: struct {
			sync.Mutex
			data map[string]interface{}
		}{
			Mutex: sync.Mutex{},
			data:  make(map[string]interface{}),
		},
		BindIp:                bindIp,
		BindPort:              bindPort,
		LastByteBuffer:        bytes.NewBuffer([]byte{}),
		NewReceivedByteBuffer: make(chan []byte, 2),
		SendData:              make(chan Packet, 2),
		config:                config,
	}

	go func() {
		ctx.decodeRunnable()
	}()
	go func() {
		ctx.sendRunnable()
	}()
	go func() {
		ctx.heartbeatTimeoutRunnable()
	}()

	return ctx
}

func (ctx *ChannelContext) Set(key string, val interface{}) {
	ctx.props.Mutex.Lock()
	defer ctx.props.Mutex.Unlock()
	ctx.props.data[key] = val
}

func (ctx *ChannelContext) Get(key string) (interface{}, error) {
	ctx.props.Mutex.Lock()
	defer ctx.props.Mutex.Unlock()
	if val, ok := ctx.props.data[key]; ok {
		return val, nil
	} else {
		return nil, errors.New("key不存在")
	}
}

func (ctx *ChannelContext) Clear() {
	ctx.props.Mutex.Lock()
	defer ctx.props.Mutex.Unlock()
	ctx.props.data = make(map[string]interface{})
}

func (ctx *ChannelContext) decodeRunnable() {
	for {
		select {
		case newVal, more := <-ctx.NewReceivedByteBuffer:
			if !more {
				return
			}
			ctx.LastByteBuffer.Write(newVal)
			ctx.decode()
		}
	}
}

func (ctx *ChannelContext) decode() {
	packet, err := ctx.config.GetGioHandler().Decode(ctx.LastByteBuffer, int32(ctx.LastByteBuffer.Len()), ctx)
	if err != nil {
		fmt.Println("err:", err)
	}
	if packet != nil {
		ctx.config.GetGioHandler().Handler(packet, ctx)
	}
	if ctx.LastByteBuffer.Len() > 0 {
		packet, err := ctx.config.GetGioHandler().Decode(ctx.LastByteBuffer, int32(ctx.LastByteBuffer.Len()), ctx)
		if err != nil {
			fmt.Println("err:", err)
		}
		if packet != nil {
			ctx.config.GetGioHandler().Handler(packet, ctx)
		}
		if ctx.LastByteBuffer.Len() > 0 {
			packet, err := ctx.config.GetGioHandler().Decode(ctx.LastByteBuffer, int32(ctx.LastByteBuffer.Len()), ctx)
			if err != nil {
				fmt.Println("err:", err)
			}
			if packet != nil {
				ctx.config.GetGioHandler().Handler(packet, ctx)
			}
			if ctx.LastByteBuffer.Len() > 0 {

			}
		}
	}
}

func (ctx *ChannelContext) sendRunnable() {
	for {
		select {
		case newVal, more := <-ctx.SendData:
			if !more {
				return
			}
			buffer := ctx.config.GetGioHandler().Encode(newVal, ctx)
			item, err := ctx.Get("conn")
			if err != nil {
				fmt.Println("err:", err)
			}
			var conn = item.(net.Conn)
			n, _ := conn.Write(buffer.Bytes())
			log.SugarLogger.Info("通过net.conn写了N多的数据:", n)
		}
	}
}

func (ctx *ChannelContext) heartbeatTimeoutRunnable() {
	timeout := ctx.config.GetHeartbeatTimeout()
	if timeout <= 0 {
		return
	}
	for {
		timer := time.NewTimer(time.Duration(timeout) * time.Millisecond)
		select {
		case <-timer.C:
			packet := &heartbeatPacket{}
			ctx.SendData <- packet
			log.SugarLogger.Info("发送一次心跳~~")
		}
	}
}
